package com.shopng.geode.api;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientCache;
import org.apache.geode.cache.client.ClientCacheFactory;
import org.apache.geode.cache.client.ClientRegionFactory;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.apache.geode.cache.client.Pool;
import org.apache.geode.cache.execute.Execution;
import org.apache.geode.cache.execute.FunctionService;
import org.apache.geode.cache.execute.ResultCollector;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.pdx.JSONFormatter;
import org.apache.geode.pdx.PdxInstance;
import org.springframework.util.Assert;

import com.shopng.geode.common.Constants;
import com.shopng.geode.common.IPAddress;
import com.shopng.geode.common.JsonUtils;
import com.shopng.utils.LogUtil;


/**
 * @author sgao20
 * supply basic operation on client side
 *         
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public class ClientService
{
  public static ClientService instance = new ClientService();
  
  public ClientService getInstance()
  {
    return instance;
  }
  /**
   * init cache with pool.
   * Just for client the first pool. when the client cache is null.
   * @param gridInfo
   * @param attributes
   * @return
   */
  public Pool initCacheWithPool(IPAddress[] ipAddresses) 
  {
      LogUtil.getCoreLog().debug("start to create client cache with pool");
      ClientCacheFactory cf = new ClientCacheFactory();
      for (IPAddress ip : ipAddresses)
      {
          cf.addPoolLocator(ip.getHost(), ip.getPort());
      }
//      cf.setPoolPRSingleHopEnabled(attributes.isPrSingleHopEnabled());
//      cf.setPoolMultiuserAuthentication(attributes.isMultiuserAuthentication());
//      cf.setPoolReadTimeout(attributes.getReadTimeout());
//      cf.setPoolRetryAttempts(attributes.getRetryAttempts());
//      cf.setPoolMaxConnections(attributes.getMaxConnections());
//      cf.setPoolMinConnections(attributes.getMinConnections());
//      cf.setPoolFreeConnectionTimeout(attributes.getFreeConnectionTimeout());
//      cf.setPoolSubscriptionEnabled(attributes.isSubscriptionEnabled());
//      cf.setPdxReadSerialized(attributes.isPdxReadSerialized());
//      PoolFactoryImpl pf = buildPoolFactory(ipAddresses, attributes);
//      PoolFactoryImpl.PoolAttributes poolAttributes = pf.getPoolAttributes();
//      Pool pool = PoolImpl.create(PoolManagerImpl.getPMI(), poolName, poolAttributes);
      ClientCache c = cf.create();
      Pool pool = c.getDefaultPool();
      createAllClientRegions(pool);
      return pool;
  }
  
  public GemFireCacheImpl getClientCache()
  {
      return GemFireCacheImpl.getInstance();
  }
  
  public List<String> createAllClientRegions(Pool pool) 
  {
    ClientRegionFactory<?, ?> clientRegionFactory = getClientCache().createClientRegionFactory(ClientRegionShortcut.PROXY);
    clientRegionFactory.setPoolName(pool.getName());
    
    List<String> fullPathList = listAllPathFromGrid(pool);
    List<String> names = new ArrayList<>();
    if (fullPathList != null)
    {
        for (String fullPath : fullPathList)
        {
            createProxyRegion(fullPath, clientRegionFactory);
            LogUtil.getCoreLog().info("Create proxy regions for regions:{}", fullPath);
        }
    }
    return names;
}
  
  public <T> void put(String path,String key,T value)
  {
    String json = JsonUtils.objectToJson(value);
    Region r = getRegion(path);
    Assert.notNull(r);
    PdxInstance pdxInstance = JSONFormatter.fromJSON(json);
    r.put(key, pdxInstance);
  }
  
  /**
   * list all path in cache server
   * @param pool
   * @return
   */
  public List<String> listAllPathFromGrid(Pool pool){
    Map<String,Object> param = new HashMap<String,Object>();
    param.put(Constants.methodName, "listRegions");
    Execution execution = FunctionService.onServer(pool).withArgs(param);
    ResultCollector rc = execution.execute("com.shopng.server.functions.UtilsFunction");
    List<?> list = (List<?>)rc.getResult();
    if(list!=null && list.size()>0)
      return (List<String>)list.get(0);
    return new ArrayList<String>();
}
  /**
  *
  * @param regionName
  * @param clientRegionFactory
  * @return
  */
 public Region createProxyRegion(String regionName, ClientRegionFactory<?, ?> clientRegionFactory)
 {
     String[] names = regionName.split("/");
     Region pr = null;
     Region r = null;
     for (String name: names)
     {
         if (StringUtils.isEmpty(name)) continue;
         try
         {
             if (pr == null)
             {
                 r = getClientCache().getRegion(name);
                 if (r != null)
                 {
                     pr = r;
                     continue;
                 }
                 r = clientRegionFactory.create(name);
             }
             else
             {
                 r = pr.getSubregion(name);
                 if (r != null)
                 {
                     pr = r;
                     continue;
                 }
                 r = clientRegionFactory.createSubregion(pr, name);
             }

         }
         catch (Exception e)
         {
             LogUtil.getCoreLog().warn("Create client region failed. path {}, name is {}", regionName, name, e);
         }
         pr = r;
     }
     return r;
 }
  
  public Region<?,?> getRegion(String path)
  {
    return getClientCache().getRegion(path);
  }
}
